﻿// CoreCalc, a spreadsheet core implementation
// ----------------------------------------------------------------------
// Copyright (c) 2006-2009 Peter Sestoft

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

//  * The above copyright notice and this permission notice shall be
//    included in all copies or substantial portions of the Software.

//  * The software is provided "as is", without warranty of any kind,
//    express or implied, including but not limited to the warranties of
//    merchantability, fitness for a particular purpose and
//    noninfringement.  In no event shall the authors or copyright
//    holders be liable for any claim, damages or other liability,
//    whether in an action of contract, tort or otherwise, arising from,
//    out of or in connection with the software or the use or other
//    dealings in the software.
// ----------------------------------------------------------------------

using System;
using System.Text;
using SCG = System.Collections.Generic;
using C5;

namespace CoreCalc.SheetDefinedFunctions
{
  public abstract class PathCond
  {
    public static readonly PathCond FALSE = new Disj();
    public static readonly PathCond TRUE  = new Conj();

    public abstract PathCond And(PathCond other);
    public abstract PathCond Or(PathCond other);
    public abstract PathCond Not();
    public abstract bool Is(bool b);
  }

  // ----------------------------------------------------------------

  public class Atom : PathCond
  {
    private readonly CGExpr cond;

    public Atom(CGExpr cond)
    {
      this.cond = cond;
    }

    public override PathCond Not()
    {
      return new Nega(this);
    }

    public override PathCond And(PathCond other)
    {
      return new Conj(this, other);
    }

    public override PathCond Or(PathCond other)
    {
      return new Disj(this, other);
    }

    public override bool Is(bool b)
    {
      return false;
    }

    public override string ToString()
    {
      return cond.ToString();
    }
  }

  // ----------------------------------------------------------------

  public class Nega : PathCond
  {
    private readonly PathCond cond;

    public Nega(PathCond cond)
    {
      this.cond = cond;
    }

    public override PathCond Not()
    {
      return cond;
    }

    public override PathCond And(PathCond other)
    {
      return new Conj(this, other);
    }

    public override PathCond Or(PathCond other)
    {
      return new Disj(this, other);
    }

    public override bool Is(bool b)
    {
      return cond.Is(!b);
    }

    public override string ToString()
    {
      return "NOT(" + cond + ")";
    }
  }

  // ----------------------------------------------------------------

  public class Conj : PathCond
  {
    private readonly IList<PathCond> conds;

    public Conj(params PathCond[] conds)
    {
      this.conds = new LinkedList<PathCond>();
      this.conds.AddAll(conds);
    }

    public override PathCond Not()
    {
      return new Nega(this);
    }

    public override PathCond And(PathCond other)
    {
      if (other.Is(false))
        return PathCond.FALSE;
      else if (other.Is(true))
        return this;
      else
      {
        Conj result = new Conj();
        result.conds.AddAll(conds);
        if (other is Conj)
          result.conds.AddAll((other as Conj).conds);
        else
          result.conds.Add(other);
        return result;
      }
    }

    public override PathCond Or(PathCond other)
    {
      return new Disj(this, other);
    }

    public override bool Is(bool b)
    { // Conj() represents TRUE
      return b == conds.IsEmpty;
    }

    public override string ToString()
    {
      if (conds.Count == 0)
        return "TRUE";
      else
      {
        bool first = true;
        StringBuilder sb = new StringBuilder();
        sb.Append("(");
        foreach (PathCond p in conds)
        {
          if (!first)
          {
            sb.Append(" && ");
            first = false;
          }
          sb.Append(p);
        }
        return sb.Append(")").ToString();
      }
    }
  }

  // ----------------------------------------------------------------

  public class Disj : PathCond
  {
    private readonly IList<PathCond> conds;

    public Disj(params PathCond[] conds)
    {
      this.conds = new LinkedList<PathCond>();
      this.conds.AddAll(conds);
    }

    public override PathCond Not()
    {
      return new Nega(this);
    }

    public override PathCond And(PathCond other)
    {
      return new Conj(this, other);
    }

    public override PathCond Or(PathCond other)
    {
      if (other.Is(true))
        return PathCond.TRUE;
      else if (other.Is(false))
        return this;
      else
      {
        Disj result = new Disj();
        result.conds.AddAll(conds);
        if (other is Disj)
          result.conds.AddAll((other as Disj).conds);
        else
          result.conds.Add(other);
        return result;
      }
    }

    public override bool Is(bool b)
    { // Disj() represents FALSE
      return b != conds.IsEmpty;
    }

    public override string ToString()
    {
      if (conds.Count == 0)
        return "FALSE";
      else
      {
        bool first = true;
        StringBuilder sb = new StringBuilder();
        sb.Append("(");
        foreach (PathCond p in conds)
        {
          if (!first)
          {
            sb.Append(" || ");
            first = false;
          }
          sb.Append(p);
        }
        return sb.Append(")").ToString();
      }
    }
  }
}